package com.googlecode.cswish.security;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Service;

import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.struts.spring.Executor;

@Service
public class UserTracerService {
	
	// see License.maxUserCount
	private int maxEnabledUserCount = 0;

	// UserId --> count, count means user logins from ${count} different places
	// 0 : user is valid, but user is offline
	// -1: prepare to remove the user
	private Map<String, Integer> onlineUsers = new ConcurrentHashMap<String, Integer>();
	
	private Map<String, HttpSession> sessionMap = new ConcurrentHashMap<String, HttpSession>();
	
	// add the event when the system startup
	private List<Executor<Boolean, String>> logoutEvents = new ArrayList<>();

	public void subscribeLogoutEvent(Executor<Boolean, String> event) {
		logoutEvents.add(event);
	}
	
	public void login(String userId, boolean online) {
		Integer count = onlineUsers.get(userId);
		if (count == null || count < 1) {
			if (online) {
				onlineUsers.put(userId, 1);
			} else {
				onlineUsers.put(userId, 0);
			}
		} else {
			if (online) {
				onlineUsers.put(userId, count + 1);
			}
		}
		
		if (maxEnabledUserCount > 0) {
			// quick check, don't think about the user status
			if (onlineUsers.size() > maxEnabledUserCount) {
				throw new ApplicationException("errors.maxEnableUser", maxEnabledUserCount);
			}
		}
	}
	
	public void logout(String userId) {
		Integer count = onlineUsers.get(userId);
		if (count != null) {
			if (count >= 1) {
				if (count > 1) {
					onlineUsers.put(userId, count - 1);
				} else {
					onlineUsers.put(userId, 0);
				}
				
				if (!logoutEvents.isEmpty()) {
					for (Executor<Boolean, String> event : logoutEvents) {
						event.execute(userId);
					}
				}
			}
		}
	}
	
	public void switchToInvalid(String userId) {
		onlineUsers.put(userId, -1);
	}
	
	public void invalid(String userId) {
		onlineUsers.remove(userId);
	}
	
	public Integer getOnlineCount(String userId) {
		return onlineUsers.get(userId);
	}
	
	public void setMaxEnabledUserCount(int maxEnabledUserCount) {
		this.maxEnabledUserCount = maxEnabledUserCount;
	}

	public void sessionCreated(HttpSession session) {
		sessionMap.put(session.getId(), session);
	}

	public void sessionDestroyed(HttpSession session) {
		sessionMap.remove(session.getId());
		
		// TODO: trace the logout event?
	}
	
	public HttpSession getSession(String sessionId) {
		return sessionMap.get(sessionId);
	}
}
